package service

import (
	"context"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"google.golang.org/grpc"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"
	"week13/api/proto"
	"week13/configs"
	"week13/errors"
	"week13/internal/biz/auth"
	"week13/log"
	"week13/metrics"
)

type AuthService struct {
}
//渠道token获取
func (srv *AuthService)  GetToken(ctx context.Context,request *proto.TokenRequest) (*proto.TokenResponse, error){
	resp := &proto.TokenResponse{}
	if request.AgentId == 0  || request.Secret == ""{
		return resp,errors.NewIllegalParam("参数错误")
	}
	authe :=  auth.NewAuthenticate()
	token, err := authe.Token(request.AgentId,request.Secret)
	if err != nil {
		log.Info(err.Error())
		resp.Code = errors.Code(err)
		resp.Message = "auth fail"
		metrics.IncrServerRequest("auth/GetToken",resp.Code)
		return resp,nil
	}
	resp.Token = token
	return resp, nil
}
//渠道鉴权
func (srv *AuthService) Auth(ctx context.Context, request *proto.AuthRequest) (*proto.AuthResponse, error){
	authe :=  auth.NewAuthenticate()
	agentId,err := authe.Auth(request.Token)
	resp := &proto.AuthResponse{Code: 0,Message: ""}
	if err != nil {
		log.Info(err.Error())
		resp.Code = 10001
		resp.Message = "auth fail,"+err.Error()
		metrics.IncrServerRequest("auth/GetToken",resp.Code)
		return resp,nil
	}
	resp.AgentId = agentId
	return resp,nil
}
//运营后台登录
func (srv *AuthService) AdminLogin(ctx context.Context, request *proto.LoginRequest) (*proto.TokenResponse, error){
	resp := proto.TokenResponse{
		Code:    0,
		Message: "",
		Token:   "",
	}
	if request.Name == "admin" && request.Passwd =="123456" {
		jwtToken, err := CreateJWTToken("1","adminSecret",time.Hour*2)
		if err != nil {
			log.Info(err.Error())
			resp.Code = 10001
			resp.Message = "system can't generate token"
			metrics.IncrServerRequest("auth/AdminLogin",resp.Code)
			return &resp, nil
		}
		resp.Token = jwtToken
		return &resp,nil
	}
	resp.Code = 10001
	resp.Message = "user or password error"
	return &resp,nil
}
//运营后台鉴权
func (srv *AuthService) AdminAuth(ctx context.Context, request *proto.AuthRequest) (*proto.AdminAuthResponse, error){
	resp := proto.AdminAuthResponse{
		Code:    0,
		Message: "",
		Uid:     0,
	}
	uid, err := ParseToken(request.Token,"adminSecret")
	log.Info("uid:%d",uid)
	if err != nil {
		log.Info(err.Error())
		resp.Code = 10001
		resp.Message = "auth fail,"+err.Error()
		metrics.IncrServerRequest("auth/AdminAuth",resp.Code)
		return &resp,nil
	}
	resp.Uid = 1
	return &resp,nil
}
type Claims struct {
	Uid string
	jwt.StandardClaims
}
func CreateJWTToken(uid string, secret string,exp time.Duration) (string,error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(exp)
	claims := Claims{
		uid,
		jwt.StandardClaims{ExpiresAt: expireTime.Unix()},
	}
	at := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := at.SignedString([]byte(secret))
	if err != nil {
		return "", err
	}
	return token, nil
}
func ParseToken(token string, secret string) (string, error) {
	tokenClaims,err := jwt.ParseWithClaims(token,&Claims{},func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})
	if err != nil {
		return "", err
	}
	if tokenClaims != nil {
		if claims,ok:=tokenClaims.Claims.(*Claims);ok && tokenClaims.Valid{
			return claims.Uid,nil
		}
	}
	return "", errors.NewTokenFail()

}


type AuthServer struct {
	stop chan int
	signs chan os.Signal
	config configs.AppConfig
}

func (srv *AuthServer) Run(ctx context.Context) error  {
	listener, err := net.Listen("tcp", srv.config.Addr)
	if err != nil {
		log.Error("auth net.listen err:%v", err)
		return err
	}
	log.Info(" auth net listening %s",srv.config.Addr)
	rpcServer := grpc.NewServer()
	proto.RegisterAuthSrvServer(rpcServer,&AuthService{})
	signal.Notify(srv.signs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		select {
		case <-srv.stop:
			rpcServer.Stop()
		case <-srv.signs:
			rpcServer.Stop()
		case <-ctx.Done():
			rpcServer.Stop()
		}
	}()
	fmt.Println("start auth server:",srv.config.Addr)
	err = rpcServer.Serve(listener)
	log.Error("auth grpc server err: %v", err)
	return err
}
func (srv *AuthServer) Close()  {
	close(srv.stop)
}
func NewAuthServer(config configs.AppConfig) *AuthServer{
	server := &AuthServer{}
	server.stop = make(chan int, 1)
	server.signs = make(chan os.Signal, 1)
	server.config = config
	return server
}